package com.gorillalogic.scrummonkey.test {
	import com.gorillalogic.scrummonkey.dms.DmsUtils;
	import com.gorillalogic.scrummonkey.dms.Iteration;
	import com.gorillalogic.scrummonkey.dms.IterationService;
	import com.gorillalogic.scrummonkey.dms.Objective;
	import com.gorillalogic.scrummonkey.dms.ObjectiveService;
	import com.gorillalogic.scrummonkey.dms.Project;
	import com.gorillalogic.scrummonkey.dms.ProjectService;
	import com.gorillalogic.scrummonkey.dms.Release;
	import com.gorillalogic.scrummonkey.dms.ReleaseService;
	
	import mx.collections.ArrayCollection;
	
	import org.flexunit.Assert;
	import org.hamcrest.assertThat;
	import org.hamcrest.collection.everyItem;
	import org.hamcrest.collection.hasItems;
	import org.hamcrest.object.equalTo;

	/**
	 * Create P, initiate Data Management, then:
	 *   1. Create R
	 *   2. Create I
	 *   3. Create O under I (and thus R & P)
	 *   4. Create O under R (and thus P)
	 */
	public class ObjectiveTest5 extends BaseTest {

		/**
		 * Create P
		 */
		[Test(async,order=1)]
		public function create():void {
			var p:Project = new Project('proj1');

			callAndThenOrFault(ProjectService.service.createProject(p), create2, null, 5000);
		}

		private function create2(data:Object, passThroughData:Object):void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), create3);
		}

		private function create3(data:Object, passThroughData:Object):void {
			var project:Project = (data.result as ArrayCollection).getItemAt(0) as Project;
			Assert.assertEquals('Bad project name', 'proj1', project.name);
			Assert.assertEquals('Bad number of releases', 0, project.releases.length);
		}


		/**
		 * Create R under data managed P
		 */
		[Test(async,order=2)]
		public function createRelease():void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), createRelease2);
		}

		private function createRelease2(data:Object, passThroughData:Object):void {
			var project:Project = (data.result as ArrayCollection).getItemAt(0) as Project;
			Assert.assertEquals('Bad project name', 'proj1', project.name);

			var r1:Release = new Release('rel1');

			//Normally, we can just call these operations and forget about it,
			//but if we must guarantee completion...then use a transaction
			callAndThenOrFault(DmsUtils.doTxn(ProjectService.service,
				function ():void {
					r1.project = project;
					ReleaseService.service.createRelease(r1);
				}), createRelease3);
		}

		private function createRelease3(data:Object, passThroughData:Object):void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), createRelease4);
		}

		private function createRelease4(data:Object, passThroughData:Object):void {
			var project:Project = (data.result as ArrayCollection).getItemAt(0) as Project;
			Assert.assertEquals('Bad project name', 'proj1', project.name);

			//verify releases
			Assert.assertEquals('Bad number of releases', 1, project.releases.length);
			var release:Release = project.releases.getItemAt(0) as Release;
			Assert.assertEquals('Bad release name', 'rel1', release.name);
			Assert.assertEquals("Bad release's project name", 'proj1', release.project.name);
		}


		/**
		 * Create I under data managed R (and thus P)
		 */
		[Test(async,order=3)]
		public function createIteration():void {
			callAndThenOrFault(ReleaseService.service.getByName('rel1'), createIteration2);
		}

		private function createIteration2(data:Object, passThroughData:Object):void {
			var release:Release = (data.result as ArrayCollection).getItemAt(0) as Release;
			Assert.assertEquals('Bad release name', 'rel1', release.name);

			var i1:Iteration = new Iteration('it1');

			//Normally, we can just call these operations and forget about it,
			//but if we must guarantee completion...then use a transaction
			callAndThenOrFault(DmsUtils.doTxn(ReleaseService.service,
				function ():void {
					i1.release = release;
					IterationService.service.createIteration(i1);
				}), createIteration3);
		}

		private function createIteration3(data:Object, passThroughData:Object):void {
			callAndThenOrFault(ReleaseService.service.getByName('rel1'), createIteration4);
		}

		private function createIteration4(data:Object, passThroughData:Object):void {
			var release:Release = (data.result as ArrayCollection).getItemAt(0) as Release;
			Assert.assertEquals('Bad release name', 'rel1', release.name);

			//verify iterations
			Assert.assertEquals('Bad number of iterations', 1, release.iterations.length);
			var iteration:Iteration = release.iterations.getItemAt(0) as Iteration;
			Assert.assertEquals('Bad iteration name', 'it1', iteration.name);
			Assert.assertEquals("Bad iteration's release name", 'rel1', iteration.release.name);
		}


		/**
		 * Create O under data managed I (and thus R & P)
		 */
		[Test(async,order=4)]
		public function createObjectiveA():void {
			callAndThenOrFault(IterationService.service.getByName('it1'), createObjectiveA2);
		}

		private function createObjectiveA2(data:Object, passThroughData:Object):void {
			var iteration:Iteration = (data.result as ArrayCollection).getItemAt(0) as Iteration;
			Assert.assertEquals('Bad iteration name', 'it1', iteration.name);

			var o1:Objective = new Objective('obj1');

			//Normally, we can just call these operations and forget about it,
			//but if we must guarantee completion...then use a transaction
			callAndThenOrFault(DmsUtils.doTxn(IterationService.service,
				function ():void {
					o1.assign(iteration);
					ObjectiveService.service.createObjective(o1);
				}), createObjectiveA3);
		}

		private function createObjectiveA3(data:Object, passThroughData:Object):void {
			trace('createObjectiveA3');
			callAndThenOrFault(IterationService.service.getByName('it1'), createObjectiveA4);
		}

		private function createObjectiveA4(data:Object, passThroughData:Object):void {
			var iteration:Iteration = (data.result as ArrayCollection).getItemAt(0) as Iteration;
			Assert.assertEquals('Bad iteration name', 'it1', iteration.name);

			//verify objectives
			Assert.assertEquals('Bad number of objectives', 1, iteration.objectives.length);
			var objective:Objective = iteration.objectives.getItemAt(0) as Objective;
			Assert.assertEquals('Bad objective name', 'obj1', objective.name);
			Assert.assertEquals("Bad objective's project name", 'proj1', objective.project.name);
			Assert.assertEquals("Bad objective's release name", 'rel1', objective.release.name);
			Assert.assertEquals("Bad objective's iteration name", 'it1', objective.iteration.name);
		}


		/**
		 * Create O under data managed R (and thus P)
		 */
		[Test(async,order=5)]
		public function createObjectiveB():void {
			callAndThenOrFault(ReleaseService.service.getByName('rel1'), createObjectiveB2);
		}

		private function createObjectiveB2(data:Object, passThroughData:Object):void {
			var release:Release = (data.result as ArrayCollection).getItemAt(0) as Release;
			Assert.assertEquals('Bad release name', 'rel1', release.name);

			var o2:Objective = new Objective('obj2');

			//Normally, we can just call these operations and forget about it,
			//but if we must guarantee completion...then use a transaction
			callAndThenOrFault(DmsUtils.doTxn(IterationService.service,
				function ():void {
					o2.assign(release);
					ObjectiveService.service.createObjective(o2);
				}), createObjectiveB3);
		}

		private function createObjectiveB3(data:Object, passThroughData:Object):void {
			trace('createObjectiveB3');
			callAndThenOrFault(ReleaseService.service.getByName('rel1'), createObjectiveB4);
		}

		private function createObjectiveB4(data:Object, passThroughData:Object):void {
			var release:Release = (data.result as ArrayCollection).getItemAt(0) as Release;
			Assert.assertEquals('Bad release name', 'rel1', release.name);

			//verify iterations
			Assert.assertEquals('Bad number of iterations', 1, release.iterations.length);
			var iteration:Iteration = release.iterations.getItemAt(0) as Iteration;
			Assert.assertEquals('Bad iteration name', 'it1', iteration.name);
			Assert.assertEquals("Bad iteration's release name", 'rel1', iteration.release.name);

			//verify objectives on release
			Assert.assertEquals('Bad number of objectives', 2, release.objectives.length);
			var o1:Objective = release.objectives.getItemAt(0) as Objective;
			var o2:Objective = release.objectives.getItemAt(1) as Objective;
			assertThat('Bad objective names', [o1.name,o2.name], hasItems('obj1', 'obj2'));
			assertThat("Bad objective's project name", [o1.project.name,o2.project.name], everyItem(equalTo('proj1')));
			assertThat("Bad objective's release name", [o1.release.name,o2.release.name], everyItem(equalTo('rel1')));

			if (o1.name == 'obj1') {
				Assert.assertEquals("Bad objective's iteration name", 'it1', o1.iteration.name);
				Assert.assertNull("Bad objective's iteration", o2.iteration);
			} else {
				Assert.assertEquals("Bad objective's iteration name", 'it1', o2.iteration.name);
				Assert.assertNull("Bad objective's iteration", o1.iteration);
			}
		}


		/**
		 * Cleanup everything
		 */
		[Test(async,order=6)]
		public function cleanup():void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), cleanup2);
		}

		private function cleanup2(data:Object, passThroughData:Object):void {
			var project:Project = (data.result as ArrayCollection).getItemAt(0) as Project;
			Assert.assertEquals('Bad project name', 'proj1', project.name);

			callAndThenOrFault(ProjectService.service.deleteProject(project), cleanup3);
		}

		private function cleanup3(data:Object, passThroughData:Object):void {
			callAndThenOrFault(ProjectService.service.getByName('proj1'), cleanup4);
			callAndThenOrFault(ReleaseService.service.getByName('rel1'), cleanup4);
			callAndThenOrFault(IterationService.service.getByName('it1'), cleanup4);
			callAndThenOrFault(ObjectiveService.service.getByName('obj1'), cleanup4);
			callAndThenOrFault(ObjectiveService.service.getByName('obj2'), cleanup4);
		}

		private function cleanup4(data:Object, passThroughData:Object):void {
			var entities:ArrayCollection = data.result as ArrayCollection;
			Assert.assertEquals('Cleanup failed', 0, entities.length);
		}
	}
}